package myJs.user.mission.plot

import myJs.Implicits._
import myJs.Utils._
import myJs.myPkg.{BootstrapSliderOptions, LayerOptions}
import myJs.myPkg.jquery._
import myJs.tool.Pojo._
import myJs.tool.Tool
import myJs.tool.Tool._
import org.scalajs.dom.ext.Ajax
import org.scalajs.dom.{Element, Event, document}
import scalatags.Text.all._

import org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits._
import scala.scalajs.js
import scala.scalajs.js.{Date, JSON}
import scala.scalajs.js.Dynamic.{global => g}
import scala.scalajs.js.JSConverters._
import myJs.Implicits._
import myJs.components.NodeLabelComponent
import myJs.myPkg.cytoscape._
import myJs.user.FeatureSelectionTool
import myJs.user.workflowMission.featureSelectionResult.FeatureSelectionResult

import scala.collection.immutable.SeqMap
import scala.scalajs.js.JSConverters._

/**
 * Created by yz on 2019/4/25
 */
object CorNetworkTab {

  val curRPlot = CorNetwork
  val tabId = curRPlot.name
  var onlyNode1: Boolean = true

  def init(idStr: String = "", onlyNode1: Boolean = true) = {
    this.onlyNode1 = onlyNode1
    if (onlyNode1) {
      $(s"#${tabId} #node2").hide()
    } else {
      $(s"#${tabId} #node2").show()
    }
    bootStrapValidator
    refreshGShape
    Tool.initMiniColors
    refreshConfig(idStr)
    registerEvent
  }

  def refreshSlider(dict: JDS) = {
    val nodeSizeRange = dict("nodeSizeRange").split(",").toList.map(_.toInt).toJSArray
    val options = BootstrapSliderOptions.tooltip("always").value(nodeSizeRange).min(0).max(30)
    Tool.getInputByName("nodeSizeRange").slider(options)
    refreshLineWidthRange(dict)
  }

  def refreshLineWidthRange(dict: JDS)={
    val edgeWidthRange = dict("edgeWidthRange").split(",").toList.map(_.toInt).toJSArray
    val options = BootstrapSliderOptions.tooltip("always").value(edgeWidthRange).min(0).max(10)
    Tool.getInputByName("edgeWidthRange").slider(options)
  }

  def refreshNodeLabel(dict: JDS) = {
    val element1 = document.querySelector("#node1Label")
    val state1 = NodeLabelComponent.State(showNodeLabel = dict("showNode1Label").toBoolean,
      nodeFont = dict("node1Font"), nodeLabelColor = dict("node1LabelColor"), showNodeLabelName = "showNode1Label",
      nodeFontName = "node1Font", nodeLabelColorName = "node1LabelColor")
    NodeLabelComponent.component(state1)().renderIntoDOM(element1)
    val element2 = document.querySelector("#node2Label")
    val state2 = NodeLabelComponent.State(showNodeLabel = dict("showNode2Label").toBoolean,
      nodeFont = dict("node2Font"), nodeLabelColor = dict("node2LabelColor"), showNodeLabelName = "showNode2Label",
      nodeFontName = "node2Font", nodeLabelColorName = "node2LabelColor")
    NodeLabelComponent.component(state2)().renderIntoDOM(element2)
  }

  def refreshGShape = {
    val data = SeqMap("ellipse" -> messages("ellipse"), "triangle" -> messages("triangle"),
      "round-triangle" -> messages("roundTriangle"), "rectangle" -> messages("rectangle"),
      "round-rectangle" -> messages("roundRectangle"), "bottom-round-rectangle" -> messages("bottomRoundRectangle"),
      "cut-rectangle" -> messages("cutRectangle"),
      "barrel" -> messages("barrel"), "rhomboid" -> messages("rhomboid"), "diamond" -> messages("diamond"),
      "round-diamond" -> messages("roundDiamond"), "pentagon" -> messages("pentagon"),
      "round-pentagon" -> messages("roundPentagon"), "hexagon" -> messages("hexagon"),
      "round-hexagon" -> messages("roundHexagon"), "concave-hexagon" -> messages("concaveHexagon"),
      "heptagon" -> messages("heptagon"),
      "round-heptagon" -> messages("roundHeptagon"), "octagon" -> messages("octagon"),
      "round-octagon" -> messages("roundOctagon"), "star" -> messages("star"),
      "tag" -> messages("tag"), "round-tag" -> messages("roundTag"), "vee" -> messages("vee"))
    val html = data.map { case (v, text) =>
      option(value := v, text)
    }.mkString("&nbsp;")
    $(s"#${tabId} .nodeShape").html(html)
  }

  def refreshImage(idStr: String = "") = {
    val url = g.jsRoutes.controllers.MissionController.getEdgeInfo().url.toString
    val fileName = "Network_Edges_for_Cytoscape.csv"
    Ajax.get(url = s"${url.noCache}&fileName=${fileName}&missionId=${Tool.getMissionId}",
      headers = Map("Content-Type" -> "application/json")).map { xhr =>
      val rs = xhr.responseText.toJArJDS
      getNodeInfo.map { nodeRs =>
        refreshNetwork(rs, nodeRs)
      }
    }
  }

  def getNodeInfo = {
    val url = g.jsRoutes.controllers.MissionController.getCsvFileInfo().url.toString
    val fileName = "Network_Nodes_for_Cytoscape.csv"
    Ajax.get(url = s"${url.noCache}&fileName=${fileName}&missionId=${Tool.getMissionId}",
      headers = Map("Content-Type" -> "application/json")).map { xhr =>
      xhr.responseText.toJArJDS
    }
  }

  def refreshNetwork(rs: JArJDS, nodeRs: JArJDS) = {
    val degrees = nodeRs.map { dict =>
      dict("degree").toInt
    }
    val maxDegree = degrees.max
    val minDegree = degrees.min
    val degreeMap = nodeRs.map { dict =>
      dict("Node") -> dict("degree").toInt
    }.toMap
    val nodeSizeRangeJq = Tool.getInputByName("nodeSizeRange")
    val nodeSizeRange = nodeSizeRangeJq.slider("getValue").map(_ * 5)
    val edgeWidthRangeJq=Tool.getInputByName("edgeWidthRange")
    val edgeWidthRange=edgeWidthRangeJq.slider("getValue")
    val cs=rs.map{dict=>
      dict("r").toDouble.abs
    }

    val formJq = $(s"#${tabId} #form")
    val nodes = if (onlyNode1) {
      rs.flatMap { dict =>
        js.Array(dict("Node1"), dict("Node2"))
      }.map { id =>
        Node(
          data = NodeData(
            id = id,
            group = "Node1",
            score = degreeMap(id),
          )
        )
      }
    } else {
      rs.flatMap { dict =>
        js.Array(
          Node(
            data = NodeData(
              id = dict("Node1"),
              group = "Node1",
              score = degreeMap(dict("Node1")),
            )
          ),
          Node(
            data = NodeData(
              id = dict("Node2"),
              group = "Node2",
              score = degreeMap(dict("Node2")),
            )
          )
        )
      }
    }
    val edges = rs.map { dict =>
      val node1 = dict("Node1")
      val node2 = dict("Node2")
      val r = dict("r").toDouble
      val rStr = r.toFixed(3)
      Edge(
        data = EdgeData(
          id = s"${node1}${node2}",
          source = s"${node1}",
          target = s"${node2}",
          weight = r.abs,
          color = if (r>=0) "red" else "blue",
          label = s"c=${rStr},p=${dict("P").toDouble.toFixed(3)}"
        )
      )
    }
    val node2FontJq = formJq.findInputByName("node2Font")
    val node2Font = if (node2FontJq.isVisible) {
      node2FontJq.myVal
    } else "0"
    val node1FontJq = formJq.findInputByName("node1Font")
    val node1Font = if (node1FontJq.isVisible) {
      node1FontJq.myVal
    } else "0"
    val options = CytoscapeOptions(
      container = document.getElementById("cy"),
      elements = Elements(
        nodes = nodes.toList,
        edges = edges.toList
      ),
      layout = Layout(
        name = "cose",
        idealEdgeLength = 100,
        nodeOverlap = 20,
        refresh = 20,
        fit = true,
        padding = 30,
        randomize = false,
        componentSpacing = 100,
        nodeRepulsion = 400000,
        edgeElasticity = 100,
        nestingFactor = 5,
        gravity = 80,
        numIter = 1000,
        initialTemp = 200,
        coolingFactor = 0.95,
        minTemp = 1.0
      ),
      style = List(
        Style(
          selector = "node",
          style = StyleData(
            width = s"mapData(score,${minDegree},${maxDegree},${nodeSizeRange(0)},${nodeSizeRange(1)})",
            height = s"mapData(score,${minDegree},${maxDegree},${nodeSizeRange(0)},${nodeSizeRange(1)})",
            content = "data(id)",
            `font-size` = "12px",
            `text-valign` = "center",
            `text-halign` = "center",
            `text-outline-width` = "2px"
          )
        ),
        Style(
          selector = "node[group='Node1']",
          style = StyleData(
            shape = formJq.findInputByName("node1Shape").myVal,
            `background-color` = formJq.findInputByName("node1Color").myVal,
            opacity = formJq.findInputByName("node1Alpha").myVal.toDouble,
            `font-size` = s"${node1Font}px",
            color = s"${formJq.findInputByName("node1LabelColor").myVal}"
          )
        ),
        Style(
          selector = "node[group='Node2']",
          style = StyleData(
            shape = formJq.findInputByName("node2Shape").myVal,
            `background-color` = formJq.findInputByName("node2Color").myVal,
            opacity = formJq.findInputByName("node2Alpha").myVal.toDouble,
            `font-size` = s"${node2Font}px",
            color = s"${formJq.findInputByName("node2LabelColor").myVal}"
          )
        ),
        Style(
          selector = "node:selected",
          style = StyleData(
            `border-width` = "6px",
            `border-color` = "#AAD8FF",
            `border-opacity` = 0.5,
            `background-color` = "#993399",
            `text-outline-color` = "#993399"
          )
        ),
        Style(
          selector = "edge",
          style = StyleData(
            `curve-style` = "haystack",
            `haystack-radius` = 0.5,
            `line-color` = "data(color)",
            opacity = formJq.findInputByName("lineAlpha").myVal.toDouble,
            width = s"mapData(weight, ${cs.min}, ${cs.max}, ${edgeWidthRange(0)}, ${edgeWidthRange(1)})",
            `overlay-padding` = "3px"
          )
        ),
        Style(
          selector = "edge.highlighted",
          style = StyleData(
            `line-color` = s"#2a6cd6",
            `target-arrow-color` = "#2a6cd6",
            opacity = 0.7,
            label = "data(label)",
            `edge-text-rotation` = "autorotate"
          )
        ),
      ),
      minZoom = 1e-1,
      maxZoom = 5,
      wheelSensitivity = 0.05
    )
    val cy = Cytoscape(options)
    cy.unbind("click")
    cy.bind("click", (e) => {
      val ce = e.target.toElement.asInstanceOf[CytoscapeElement]
      if (ce == cy) {
        cy.edges().removeClass("highlighted")
      } else if (ce.group() == "nodes") {
        val b = cy.edges(s"[target='${ce.id()}']").hasClass("highlighted") ||
          cy.edges(s"[source='${ce.id()}']").hasClass("highlighted")
        if (b) {
          cy.edges().removeClass("highlighted")
        } else {
          cy.edges().removeClass("highlighted")
          cy.edges(s"[target='${ce.id()}']").addClass("highlighted")
          cy.edges(s"[source='${ce.id()}']").addClass("highlighted")
        }
      } else {
        if (cy.edges(s"[id='${ce.id()}']").hasClass("highlighted")) {
          cy.edges(s"[id='${ce.id()}']").removeClass("highlighted")
        } else {
          cy.edges(s"[id='${ce.id()}']").addClass("highlighted")
        }
      }
    })

  }

  def myRun = {
    val idStr = FeatureSelectionResult.getIdStr
    val bv = $(s"#${tabId} #form").bv
    bv.validate()
    val valid = bv.isValid()
    if (valid) {
      val element = div(id := "content",
        span(id := "info", messages("running"),
          span(id := "progress", "。。。")), " ",
        img(src := "/assets/images/running2.gif", cls := "runningImage", width := 30, height := 20)
      ).render
      val layerOptions = LayerOptions.title(Tool.zhInfo).closeBtn(0).skin("layui-layer-molv").btn(js.Array())
      val index = layer.alert(element, layerOptions)
      val url = g.jsRoutes.controllers.MissionToolController.plot().url.toString
      val formData = $(s"#${tabId} #form").serialize()
      Ajax.post(url = s"${url}", data = s"${formData}&kind=${curRPlot.name}&idStr=${idStr}",
        headers = Map("Content-Type" -> "application/x-www-form-urlencoded")).map { xhr =>
        val rs = xhr.responseText.toJDJAn
        layer.close(index)
        val valid = rs("valid").toB
        if (valid) {
          refreshImage(idStr)
        } else {
          g.swal("Error", rs.myGet("message"), "error")
        }
      }
    }
  }

  def registerEvent = {
    $(document.body).offThenOn("click", s"#${tabId} .myRun", "", (y: Element) => {
      myRun
    })
  }

  def refreshConfig(idStr: String = "") = {
    val fileName = curRPlot.configFileName
    val finalFileName = FeatureSelectionTool.getFinalFileName(idStr, fileName)
    val url = g.jsRoutes.controllers.MissionToolController.getConfigData().url.toString
    Ajax.get(url = s"${url.noCache}&missionId=${Tool.getMissionId}&fileName=${finalFileName}",
      headers = Map("Content-Type" -> "application/json")).map { xhr =>
      val rs = xhr.responseText.toJDJAn.toMyJDS
      Tool.fillByNames(rs, tabId)
      Tool.fillByNameAndTriggers(rs.toJDJAn, parentId = tabId)
      refreshImage(idStr)
      refreshNodeLabel(rs)
      refreshSlider(rs)
    }
  }

  def bootStrapValidator = {
    val maxNumber = Double.MaxValue
    val fieldsMap = js.Dictionary(
      "node1Color" -> {
        js.Dictionary(
          "validators" -> js.Dictionary(
            "notEmpty" -> js.Dictionary(
              "message" -> s"${messages("null")}"
            ),
          )
        )
      },
      "node1Alpha" -> {
        js.Dictionary(
          "validators" -> js.Dictionary(
            "notEmpty" -> js.Dictionary(
              "message" -> s"${messages("null")}"
            ),
            "numeric" -> js.Dictionary(
              "message" -> s"${messages("numeric")}",
            ),
            "between" -> js.Dictionary(
              "min" -> 0,
              "max" -> 1,
              "message" -> s"${messages("between", 0, 1)}",
            ),
          )
        )
      },
      "node1Font" -> {
        val info = messages("nodeLabelFontsize")
        js.Dictionary(
          "validators" -> js.Dictionary(
            "notEmpty" -> js.Dictionary(
              "message" -> s"${messages("null")}"
            ),
            "numeric" -> js.Dictionary(
              "message" -> s"${messages("numeric")}",
            ),
            "between" -> js.Dictionary(
              "min" -> 0,
              "max" -> maxNumber,
              "inclusive" -> false,
              "message" -> s"${messages("greaterThan", 0)}",
            ),
          )
        )
      },
      "lineAlpha" -> {
        js.Dictionary(
          "validators" -> js.Dictionary(
            "notEmpty" -> js.Dictionary(
              "message" -> s"${messages("null")}"
            ),
            "numeric" -> js.Dictionary(
              "message" -> s"${messages("numeric")}",
            ),
            "between" -> js.Dictionary(
              "min" -> 0,
              "max" -> 1,
              "message" -> s"${messages("between", 0, 1)}",
            ),
          )
        )
      },
      "node2Color" -> {
        js.Dictionary(
          "validators" -> js.Dictionary(
            "notEmpty" -> js.Dictionary(
              "message" -> s"${messages("null")}"
            ),
          )
        )
      },
      "node2Alpha" -> {
        js.Dictionary(
          "validators" -> js.Dictionary(
            "notEmpty" -> js.Dictionary(
              "message" -> s"${messages("null")}"
            ),
            "numeric" -> js.Dictionary(
              "message" -> s"${messages("numeric")}",
            ),
            "between" -> js.Dictionary(
              "min" -> 0,
              "max" -> 1,
              "message" -> s"${messages("between", 0, 1)}",
            ),
          )
        )
      },
      "node2Font" -> {
        val info = messages("nodeLabelFontsize")
        js.Dictionary(
          "validators" -> js.Dictionary(
            "notEmpty" -> js.Dictionary(
              "message" -> s"${messages("null")}"
            ),
            "numeric" -> js.Dictionary(
              "message" -> s"${messages("numeric")}",
            ),
            "between" -> js.Dictionary(
              "min" -> 0,
              "max" -> maxNumber,
              "inclusive" -> false,
              "message" -> s"${messages("greaterThan", 0)}",
            ),
          )
        )
      },
    ).toMap
    val finalFields = (fieldsMap).toJSDictionary
    val dict: js.Dictionary[js.Any] = js.Dictionary(
      "feedbackIcons" -> js.Dictionary(
        "valid" -> "glyphicon glyphicon-ok",
        "invalid" -> "glyphicon glyphicon-remove",
        "validating" -> "glyphicon glyphicon-refresh",
      ),
      "fields" -> finalFields
    )

    $(s"#${tabId} #form").bootstrapValidator(dict)
  }

}
